package com.thinksns.tschat.mp3;

import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.text.format.Time;
import android.util.Log;

import com.czt.mp3recorder.DataEncodeThread;
import com.czt.mp3recorder.PCMFormat;
import com.czt.mp3recorder.util.LameUtil;
import com.isure.audio.Speex;
import com.isure.audio.SpeexUtils;
import com.isure.audio.speex.OggCrc;
import com.isure.audio.speex.SpeexEncoder;
import com.thinksns.tschat.constant.TSConfig;
import com.thinksns.tschat.unit.AudioFocusManager;

import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Date;

public class VideoRecorder {
    //=======================AudioRecord Default Settings=======================
    private static final int DEFAULT_AUDIO_SOURCE = MediaRecorder.AudioSource.MIC;
    /**
     * 以下三项为默认配置参数。Google Android文档明确表明只有以下3个参数是可以在所有设备上保证支持的。
     */
    private static final int DEFAULT_SAMPLING_RATE = 8000;//模拟器仅支持从麦克风输入8kHz采样率
    private static final int DEFAULT_CHANNEL_CONFIG = AudioFormat.CHANNEL_IN_MONO;
    /**
     * 下面是对此的封装
     * private static final int DEFAULT_AUDIO_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
     */
    private static final PCMFormat DEFAULT_AUDIO_FORMAT = PCMFormat.PCM_16BIT;

    //======================Lame Default Settings=====================
    private static final int DEFAULT_LAME_MP3_QUALITY = 2;
    /**
     * 与DEFAULT_CHANNEL_CONFIG相关，因为是mono单声，所以是1
     */
    private static final int DEFAULT_LAME_IN_CHANNEL = 1;
    /**
     * Encoded bit rate. MP3 file will be encoded with bit rate 32kbps
     */
    private static final int DEFAULT_LAME_MP3_BIT_RATE = 32;

    //==================================================================
    private static final String MP3_EXTENSION = ".mp3";
    private static final String SPX_EXTENSION = ".spx";
    /**
     * 自定义 每160帧作为一个周期，通知一下需要进行编码
     */
    private static final int FRAME_COUNT = 160;
    private AudioRecord mAudioRecord = null;
    private int mBufferSize = 160;
    private boolean mIsRecording = false;
    private File mRecordFile; // MP3 文件路劲
    private File mSpxFile; // spx 文件路劲
    private Handler mHandler;
    private long startTime;
    private final Object mutex = new Object();
    private AudioFocusManager mAudioFocusManager;
    private boolean isQuite = false;

    /**
     * Default constructor. Setup recorder with default sampling rate 1 channel,
     * 16 bits pcm
     *
     * @param uiHandler target file
     */
    public VideoRecorder(Handler uiHandler) {
        mHandler = uiHandler;
        mAudioFocusManager = new AudioFocusManager();
    }

    /**
     * Start recording. Create an encoding thread. Start record from this
     * thread.
     *
     * @throws IOException initAudioRecorder throws
     */
    private void start() throws IOException {
        if (mIsRecording) {
            return;
        }
        mAudioFocusManager.requestTheAudioFocus();
        startTime = System.currentTimeMillis();
        mIsRecording = true; // 提早，防止init或startRecording被多次调用
        initAudioRecorder();
        mAudioRecord.startRecording();
        new Thread() {
            @Override
            public void run() {
                SpeexEncoder encoder = new SpeexEncoder(mSpxFile.getAbsolutePath(), mRecordFile.getAbsoluteFile());
                Thread encodeThread = new Thread(encoder);
                encoder.setRecording(true);
                encodeThread.start();

                synchronized (mutex) {
                    while (!mIsRecording) {
                        try {
                            mutex.wait();
                        } catch (InterruptedException e) {
                            throw new IllegalStateException("Wait() interrupted!", e);
                        }
                    }
                }
                android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);

                int bufferRead = 0;
                short[] tempBuffer = new short[mBufferSize];
                mAudioRecord.startRecording();

                while (mIsRecording) {
                    bufferRead = mAudioRecord.read(tempBuffer, 0, mBufferSize);
                    calculateRealVolume(tempBuffer, bufferRead);
                    // bufferRead = recordInstance.read(tempBuffer, 0, 320);
                    if (bufferRead == AudioRecord.ERROR_INVALID_OPERATION) {
                        throw new IllegalStateException("read() returned AudioRecord.ERROR_INVALID_OPERATION");
                    } else if (bufferRead == AudioRecord.ERROR_BAD_VALUE) {
                        throw new IllegalStateException("read() returned AudioRecord.ERROR_BAD_VALUE");
                    } else if (bufferRead == AudioRecord.ERROR_INVALID_OPERATION) {
                        throw new IllegalStateException("read() returned AudioRecord.ERROR_INVALID_OPERATION");
                    }
                    encoder.putData(tempBuffer, bufferRead);
                }
                encoder.setRecording(false);
                try {
                    mAudioRecord.stop();
                    mAudioRecord.release();
                    //tell encoder to stop.
                    mAudioRecord = null;
                    if (!isQuite){
                        decode();
                    }

                } catch (IOException e) {
                    e.printStackTrace();
                }finally {
                    deleteFile(mSpxFile);
                    if (isQuite){
                        deleteFile(mRecordFile);
                    }
                }
            }

        }.start();
    }

    private int mVolume;

    /**
     * 此计算方法来自samsung开发范例
     *
     * @param buffer   buffer
     * @param readSize readSize
     */
    private void calculateRealVolume(short[] buffer, int readSize) {
        double sum = 0;
        for (int i = 0; i < readSize; i++) {
            // 这里没有做运算的优化，为了更加清晰的展示代码
            sum += buffer[i] * buffer[i];
        }
        if (readSize > 0) {
            double amplitude = sum / readSize;
            mVolume = (int) Math.sqrt(amplitude);
            int voice = mVolume * 5 / 9999;
            if (voice < 0) voice = 0;
            if (voice > 5) voice = 5;
            Message msg = new Message();
            msg.what = voice;
            mHandler.sendMessageDelayed(msg, 100);
        }
    }

    public boolean isRecording() {
        return mIsRecording;
    }

    /**
     * Initialize audio recorder
     */
    private void initAudioRecorder() throws IOException {
        int minBufferSize = AudioRecord.getMinBufferSize(DEFAULT_SAMPLING_RATE,
                DEFAULT_CHANNEL_CONFIG, DEFAULT_AUDIO_FORMAT.getAudioFormat());
        int bytesPerFrame = DEFAULT_AUDIO_FORMAT.getBytesPerFrame();
        /* Get number of samples. Calculate the buffer size
         * (round up to the factor of given frame size)
		 * 使能被整除，方便下面的周期性通知
		 * */
        int frameSize = minBufferSize / bytesPerFrame;
        if (frameSize % FRAME_COUNT != 0) {
            frameSize += (FRAME_COUNT - frameSize % FRAME_COUNT);
            minBufferSize = frameSize * bytesPerFrame;
        }
        /* Setup audio recorder */
        mAudioRecord = new AudioRecord(DEFAULT_AUDIO_SOURCE,
                DEFAULT_SAMPLING_RATE, DEFAULT_CHANNEL_CONFIG, DEFAULT_AUDIO_FORMAT.getAudioFormat(),
                minBufferSize);
        /*
         * Initialize lame buffer
		 * mp3 sampling rate is the same as the recorded pcm sampling rate
		 * The bit rate is 32kbps
		 *
		 */
        LameUtil.init(DEFAULT_SAMPLING_RATE, DEFAULT_LAME_IN_CHANNEL, DEFAULT_SAMPLING_RATE, DEFAULT_LAME_MP3_BIT_RATE, DEFAULT_LAME_MP3_QUALITY);
    }

    /**
     * 放弃发送录音
     */
    public void discardRecording() {
        if (mIsRecording){
            isQuite = true;
            mIsRecording = false;
        }
    }

    /**
     * 开始录音
     *
     * @param chatName
     */
    public void startRecording(String chatName) {
        /*init record save path*/
        String path = Environment.getExternalStorageDirectory() + "/" +
                TSConfig.VOICE_PATH;
        File tmpFile = new File(path);
        if (!tmpFile.exists()) tmpFile.mkdirs();
        this.mRecordFile = new File(tmpFile.getAbsolutePath(), getVoiceFileName(chatName, MP3_EXTENSION));
        this.mSpxFile = new File(tmpFile.getAbsolutePath(), getVoiceFileName(chatName, SPX_EXTENSION));

        try {
            start();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public int stopRecoding() {
        if (mIsRecording) {
            long time = System.currentTimeMillis();
            isQuite = false;
            mIsRecording = false;
            mAudioFocusManager.releaseTheAudioFocus();
            if (this.mSpxFile != null && this.mSpxFile.exists() && this.mSpxFile.isFile()) {
                if (this.mSpxFile.length() == 0L) {
                    this.mSpxFile.delete();
                    deleteFile(mRecordFile);
                    return -1011;
                } else {
                    int var1 = (int) (time - this.startTime) / 1000;
                    Log.d("voice", "voice recording finished. seconds:" + var1 + " file length:" + this.mRecordFile.length());
                    return var1;
                }
            } else {
                return -1011;
            }
        } else {
            return 0;
        }
    }

    /**
     * 解压 SPX 将其转换为 MP3格式
     *
     * @throws IOException
     */
    public void decode() throws IOException {
        DataOutputStream mDataOutputStream = new DataOutputStream(new FileOutputStream(mRecordFile.getAbsoluteFile()));
        byte[] header = new byte[2048];
        byte[] payload = new byte[65536];
        final int OGG_HEADERSIZE = 27;
        final int OGG_SEGOFFSET = 26;
        final String OGGID = "OggS";
        int segments = 0;
        int curseg = 0;
        int bodybytes = 0;
        int decsize = 0;
        int packetNo = 0;
        // construct a new decoder
        Speex speexDecoder = new Speex();
        speexDecoder.init();
        // open the input stream
        RandomAccessFile dis = new RandomAccessFile(mSpxFile, "r");

        int origchksum;
        int chksum;
        try {
            // read until we get to EOF
            while (true) {
                dis.readFully(header, 0, OGG_HEADERSIZE);
                origchksum = SpeexUtils.readInt(header, 22);
                SpeexUtils.readLong(header, 6);
                header[22] = 0;
                header[23] = 0;
                header[24] = 0;
                header[25] = 0;
                chksum = OggCrc.checksum(0, header, 0, OGG_HEADERSIZE);

                // make sure its a OGG header
                if (!OGGID.equals(new String(header, 0, 4))) {
                    System.err.println("missing ogg id!");
                    return;
                }

				/* how many segments are there? */
                segments = header[OGG_SEGOFFSET] & 0xFF;
                dis.readFully(header, OGG_HEADERSIZE, segments);
                chksum = OggCrc.checksum(chksum, header, OGG_HEADERSIZE, segments);

				/* decode each segment, writing output to wav */
                for (curseg = 0; curseg < segments; curseg++) {

//                    if (Thread.interrupted()) {
//                        dis.close();
//                        return;
//                    }
                    /* get the number of bytes in the segment */
                    bodybytes = header[OGG_HEADERSIZE + curseg] & 0xFF;
                    if (bodybytes == 255) {
                        System.err.println("sorry, don't handle 255 sizes!");
                        return;
                    }
                    dis.readFully(payload, 0, bodybytes);
                    chksum = OggCrc.checksum(chksum, payload, 0, bodybytes);

					/* decode the segment */
                    /* if first packet, read the Speex header */
                    if (packetNo == 0) {
                        if (SpeexUtils.readSpeexHeader(payload, 0, bodybytes, true)) {

                            packetNo++;
                        } else {
                            packetNo = 0;
                        }
                    } else if (packetNo == 1) { // Ogg Comment packet
                        packetNo++;
                    } else {

						/* get the amount of decoded data */
                        short[] decoded = new short[mBufferSize];
                        if ((decsize = speexDecoder.decode(payload, decoded, mBufferSize)) > 0) {
                            byte[] mMp3Buffer = new byte[(int) (7200 + (decsize * 2 * 1.25))];
                            short[] buffer = decoded;
                            int readSize = decsize;
                            int encodedSize = LameUtil.encode(buffer, buffer, readSize, mMp3Buffer);
                            mDataOutputStream.write(mMp3Buffer, 0, encodedSize);
                            mDataOutputStream.flush();
                        }
                        packetNo++;
                    }
                }
                if (chksum != origchksum)
                    throw new IOException("Ogg CheckSums do not match");
            }
        } catch (EOFException eof) {
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            mDataOutputStream.close();
            System.out.println("release............");
            deleteFile(mSpxFile);
            LameUtil.close();
        }
        dis.close();
    }


    public String getVoiceFileName(String var1, String typeName) {
        Time var2 = new Time();
        var2.setToNow();
        return var1 + var2.toString().substring(0, 15) + typeName;
    }

    /**
     * @return MP3录音文件路径
     */
    public String getVoiceFilePath() {
        return mRecordFile.getAbsolutePath();
    }

    private void deleteFile(File file) {
        if (file != null && file.exists() && file.isFile()) {
            file.delete();
        }
    }
}